Data Cleaning
• Combine all the soil types into a single column
# Since there are 40 of the columns dedicated only to soil type, it was necessary to reduce those columns into one
df <- df %>%
mutate(Combined_Soil_Type = case_when(
Soil_Type1 == 1 ~ "1",
Soil_Type2 == 1 ~ "2",
Soil_Type3 == 1 ~ "3",
Soil_Type4 == 1 ~ "4",
Soil_Type5 == 1 ~ "5",
Soil_Type6 == 1 ~ "6",
Soil_Type7 == 1 ~ "7",
Soil_Type8 == 1 ~ "8",
Soil_Type9 == 1 ~ "9",
Soil_Type10 == 1 ~ "10",
Soil_Type11 == 1 ~ "11",
Soil_Type12 == 1 ~ "12",
Soil_Type13 == 1 ~ "13",
Soil_Type14 == 1 ~ "14",
Soil_Type15 == 1 ~ "15",
Soil_Type16 == 1 ~ "16",
Soil_Type17 == 1 ~ "17",
Soil_Type18 == 1 ~ "18",
Soil_Type19 == 1 ~ "19",
Soil_Type20 == 1 ~ "20",
Soil_Type21 == 1 ~ "21",
Soil_Type22 == 1 ~ "22",
Soil_Type23 == 1 ~ "23",
Soil_Type24 == 1 ~ "24",
Soil_Type25 == 1 ~ "25",
Soil_Type26 == 1 ~ "26",
Soil_Type27 == 1 ~ "27",
Soil_Type28 == 1 ~ "28",
Soil_Type29 == 1 ~ "29",
Soil_Type30 == 1 ~ "30",
Soil_Type31 == 1 ~ "31",
Soil_Type32 == 1 ~ "32",
Soil_Type33 == 1 ~ "33",
Soil_Type34 == 1 ~ "34",
Soil_Type35 == 1 ~ "35",
Soil_Type36 == 1 ~ "36",
Soil_Type37 == 1 ~ "37",
Soil_Type38 == 1 ~ "38",
Soil_Type39 == 1 ~ "39",
Soil_Type40 == 1 ~ "40"
))
• Doing the same for wilderness area
df <- df %>%
mutate(Combined_Wilderness_Area = case_when(
Wilderness_Area1 == 1 ~ "Rawah Area",
Wilderness_Area2 == 1 ~ "Neota Area",
Wilderness_Area3 == 1 ~ "Comanche Peak Area",
Wilderness_Area4 == 1 ~ "Cache la Poudre Area"
))
• Summing up all hillshade into one
df <- df %>%
mutate(Total_Hillshade = Hillshade_9am + Hillshade_Noon + Hillshade_3pm)
• Change degrees into cardinal directions
# Function to convert Aspect into cardinal and intermediate directions
get_cardinal_direction_full <- function(aspect) {
if (!is.na(aspect) && aspect >= 0 && aspect < 360) {
if (aspect >= 337.5 || aspect < 22.5) {
return("N")
} else if (aspect >= 22.5 && aspect < 67.5) {
return("NE")
} else if (aspect >= 67.5 && aspect < 112.5) {
return("E")
} else if (aspect >= 112.5 && aspect < 157.5) {
return("SE")
} else if (aspect >= 157.5 && aspect < 202.5) {
return("S")
} else if (aspect >= 202.5 && aspect < 247.5) {
return("SW")
} else if (aspect >= 247.5 && aspect < 292.5) {
return("W")
} else if (aspect >= 292.5 && aspect < 337.5) {
return("NW")
}
}
return("N")
}
# Apply the function to the Aspect column directly
df$Aspect <- sapply(df$Aspect, get_cardinal_direction_full)
# View the first few rows to confirm
head(df$Aspect)
[1] "NE" "NE" "SE" "SE" "NE" "SE"
• Set the values for cover type
cover_type_mapping <- c(
`1` = "Spruce/Fir",
`2` = "Lodgepole Pine",
`3` = "Ponderosa Pine",
`4` = "Cottonwood/Willow",
`5` = "Aspen",
`6` = "Douglas-fir",
`7` = "Krummholz"
)
# Replace the Cover_Type values in the dataframe
df$Cover_Type <- factor(cover_type_mapping[as.character(df$Cover_Type)], levels = cover_type_mapping)
# View the first few rows to confirm the changes
head(df$Cover_Type)
[1] Aspen Aspen Lodgepole Pine Lodgepole Pine Aspen Lodgepole Pine
Levels: Spruce/Fir Lodgepole Pine Ponderosa Pine Cottonwood/Willow Aspen Douglas-fir Krummholz
• Since the Slope ranges from 0 to 50, we can make it into continuous
value so that while plotting it makes it easier and not very messy
# Modify the Slope column to group values in bins of 5
df$Slope <- cut(df$Slope,
breaks = seq(0, max(df$Slope, na.rm = TRUE), by = 5),
include.lowest = TRUE,
right = FALSE,
labels = paste(seq(0, max(df$Slope, na.rm = TRUE) - 5, by = 5),
seq(5, max(df$Slope, na.rm = TRUE), by = 5), sep = "-"))
• Check distribution for Elevation
a <- ggplot(df, aes(Elevation)) +
geom_histogram(fill="steelblue")+
labs(title= "Distribution of Elevvation")
a

a+facet_wrap(Combined_Wilderness_Area~.) +
labs(title = "Wilderness Areas across different elevation levels")

ggplot(df, aes(Elevation)) +
geom_histogram(fill="steelblue")+
labs(title= "Distribution of Elevation") +
facet_wrap(Aspect~.)

ggplot(df, aes(x = Elevation)) +
geom_density(fill = "blue", alpha = 0.5) +
labs(title = "Density Plot of Elevation", x = "Elevation", y = "Density") +
theme_minimal()

• Since our data set is too large for proper visualization, we will
take a small sample of the data that resembles the trend of the original
data.
set.seed(123) # Setting a seed for reproducibility
df_sample <- df %>% sample_n(3000)
ggplot(df_sample, aes(x = Elevation)) +
geom_density(fill = "blue", alpha = 0.5) +
labs(title = "Density Plot of Elevation", x = "Elevation", y = "Density") +
theme_minimal()

• Check the distribution of Slope
ggplot(df, aes(Slope)) +
geom_bar()

ggplot(df, aes(x = Slope)) +
geom_density(fill = "blue", alpha = 0.5) +
labs(title = "Density Plot of Slope", x = "Slope", y = "Density") +
theme_minimal()

ggplot(df_sample, aes(x = Slope)) +
geom_density(fill = "blue", alpha = 0.5) +
labs(title = "Density Plot of Slope", x = "Slope", y = "Density") +
theme_minimal()

Since the density of slope and elevation looks similar to that of the
real dataset, we will conclude that other feature will follow the same
trend.
• Distribution of Cover Type throughout the data set
a<-ggplot(df, aes(Cover_Type)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(labels = scales::comma)+
geom_bar(fill="steelblue")+
geom_text(stat = 'count', aes(label = ..count..),
vjust = -0.5, color = "black") +
labs(title = "Distribution of Cover Type",
x="Cover Type",
y = "Count")
a

• Facet the cover distribution according to Wilderness Area
a + facet_grid(Combined_Wilderness_Area~., scales="free")

a+facet_wrap(Aspect~.)

• Check how much sunlight does each direction receive at 9am
ggplot(df, aes(Hillshade_9am))+
geom_histogram(fill="steelblue") +
facet_wrap(Aspect~.) +
labs(title = "Hillshade at 9am")

East and North received the most sunlight at 9am.
• Check how much sunlight does each direction receive during noon
ggplot(df, aes(Hillshade_Noon))+
geom_histogram(fill="steelblue") +
facet_wrap(Aspect~.) +
labs(title = "Hillshade during Noon")

During the noon time, only south west and west received the least
amount of sunlight.
• Check how much sunlight does each direction receive at 3pm
ggplot(df, aes(Hillshade_3pm))+
geom_histogram(fill="steelblue") +
facet_wrap(Aspect~.) +
labs(title = "Hillshade at 3pm")

From all the plots above, it is clear that west and southwest
received the least amount of sunlight.
• Remove the unnecessary columns Since our data set already had a
massive set of columns and the columns ‘Soil_Type’ and ‘Wilderness_Area’
have been categorized into the column ‘Combined_Soil_Type’ and
‘Combined_Wilderness_Area’ they are unnecessary
df <- df %>%
select(-starts_with("Soil_Type"), -starts_with("Wilderness_Area"), -starts_with("Hillshade")) #Remove any column that starts with the word Soil_Type, Wilderness_Area and Hillshade
• Checking to see if the changes have been applied
df
a+facet_wrap(.~Combined_Wilderness_Area)

• Plot density graph for elevation vs cover type
ggplot(df, aes(x = Elevation, fill = factor(Cover_Type))) +
geom_density(alpha = 0.5) +
labs(title = "Density of Elevation by Cover Type", x = "Elevation", y = "Density")

We can see that Cottonwood grows at lower altitude and Krummholz
grows at the higher altitude.
ggplot(df_sample, aes(x=Cover_Type, y=Elevation))+geom_boxplot()

• Check distribution for soil type
p<-ggplot(df, aes(x = Combined_Soil_Type)) +
geom_bar(fill = "steelblue") +
labs(title = "Distribution of Combined Soil Types", x = "Soil Type", y = "Count") +
theme(axis.text.x = element_text(angle = 0, hjust = 1))
p

From the bar plot, it can be seen that the most type of prevalent
soil is of type 29 which is almost 1200000. On the other hand, Most of
the soil types are almost non-existent like soil_Type 13,14,37 and many
more which indicates their presence in somewhat small amount compared to
others.
• Facet soil type distribution by wilderness area
p+facet_wrap(Combined_Wilderness_Area~.) +
labs(title = "Distribution of Soil Type across Wilderness Areas")

Rawah Area receives has the most amount of soils while Comanache Area
has the most variety of soils.
•
ggplot(df, aes(x = Cover_Type, y = Total_Hillshade, fill = Cover_Type)) +
geom_boxplot() +
labs(title = "Total Hillshade (Sunlight) Distribution by Cover Type",
x = "Cover Type",
y = "Total Hillshade (Sunlight)") + scale_y_continuous(labels = scales::comma) +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) + facet_grid(Combined_Wilderness_Area~.)

From the graph above, it seems that every type of plant can grow when
receiving more sunlight but Lodgepole Pine can grow on areas with low
sunlight of upto only 200 units.
• Create a bar for soil type and stack it with cover type
ggplot(df, aes(x = Combined_Soil_Type, fill = Cover_Type)) +
geom_bar(position = "stack") +
labs(title = "Cover Type and Soil Type", x = "Soil Type", y = "Count") +
theme(axis.text.x = element_text(angle = 0, hjust = 1))

From the bar plot we can see that soil 29 is the most suitable for
spruce/fir tree and Lodgepole Pine tree which are also the most present
type of trees on other soils compared to other plants indicating their
capacity to grow on any type of situation.
• Facet it with Wilderness Area
ggplot(df, aes(x = Combined_Soil_Type, fill = Cover_Type)) +
geom_bar(position = "stack") +
labs(title = "Cover Type and Soil Type", x = "Soil Type", y = "Count") +
facet_wrap(Combined_Wilderness_Area~.)+
theme(axis.text.x = element_text(angle = 0, hjust = 1))

• Check to see if the relation between soil type and hydrology
b<-ggplot(df, aes(x=Horizontal_Distance_To_Hydrology, y=Vertical_Distance_To_Hydrology)) + geom_smooth() + facet_wrap(~Combined_Soil_Type, scales="free") +
labs(title = "Type of Soil available near water bodies")
From the plots it is hard to determine relation between the two as
every soil type seem to be available at almost every horizontal distance
away from a body of water. Some like type 1, 16, 18, 2, 21, 8, 9, 5, 6,
7 is found in the range of 600m away from the body of water, the other
types are available 1000 meters away. As for the vertical distance from
hydrology, most are found at the range of 100-150 meters above and some
like 8 and 25 are found below the body of water. Soil type 4, 24, 27,
28, 31, 32, 38, 39 and 40 could be found above 200m above any body of
water but soil type like 7, 8, 9 and 25 are found as close as 40 meter
and even potrayed in negative indicating them being submerged in the
body of water
• Add new feature ‘Cover_Type’ as colour
ggplot(df_sample, aes(x=Horizontal_Distance_To_Hydrology, y=Vertical_Distance_To_Hydrology, colour = Cover_Type)) + geom_point() + facet_wrap(~Combined_Soil_Type, scales="free") +
labs(title = "Cover type and sources of water")

• Point graph with Elevation vs Soil Type
ggplot(df_sample, aes(y=Elevation, x=Combined_Soil_Type, colour = Cover_Type))+
geom_point()+
labs(title = "Elevation vs Soil Type")

As the type of soil increases so does the elevation.
•
# Create a boxplot
ggplot(df_sample, aes(x = Elevation, y = Total_Hillshade, colour = Cover_Type)) +
geom_point(alpha = 0.5) +
labs(title = "Sunlight Exposure (Hillshade) vs Elevation",
x = "Elevation",
y = "Total Hillshade (Sunlight)") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
facet_grid(Combined_Wilderness_Area~.)

• Check the
ggplot(df_sample, aes(x = Elevation, y = Total_Hillshade)) +
geom_point(alpha = 0.5) +
labs(title = "Sunlight Exposure (Hillshade) vs Elevation",
x = "Elevation",
y = "Total Hillshade (Sunlight)") +
theme(axis.text.x = element_text(angle = 90, hjust = 1))

•
ggplot(df, aes(y=Total_Hillshade,x =Cover_Type, fill = Cover_Type)) +
geom_boxplot() +
theme(axis.text.x = element_text(angle = 45))

• Plot a histogram forhillshade and facet it with wilderness area and
cover type
ggplot(df, aes(x = Total_Hillshade)) +
geom_histogram(position = "dodge", binwidth = 10, fill="steelblue") +
labs(title = "Distribution of Cover_Type", x = "Hill Shade(Sunlight)", y = "Count") +
theme(axis.text.x = element_text( hjust = 1)) +
facet_grid(Cover_Type~Combined_Wilderness_Area, scales = "free")

From the plot, we can see that some wilderness area is unsuitable for
some type of forest as they do not grow there at all. Eg, Spruce and ……
do not grow on wilderness area 4 at all but ….wood only grows on
wilderness area 4. If we were to look at the plot alone it would seem
like Wilderness_Area4 hosts the most forest but if we look at the count
then we can see that Wilderness_Area1 hosts the most forest. If we were
to determine the conditions of the Wilderness Area then we can determine
the type of forest that grows on such terrains.
q <- ggplot(df, aes(x = Combined_Wilderness_Area, fill = Cover_Type)) +
geom_bar(position = "stack") +
labs(title = "Distribution of Cover Type according to Wilderness Area",
x = "Wilderness Area",
y = "Count") +
geom_text(stat = 'count', aes(label = ..count..), position = position_stack(vjust = 0.5), size = 3, color = "white") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
q

ggplotly(q)
From the bar graph, we can see that the most prevalent type of forest
is Lodgepole Pine followed by Spruce/Fir which are at an noticeable
amount followed by Ponderosa Pine and so on. We can also see that the
Rawah Area hosts the most number of plants while the Comanche Peak Area
hosts the most type of plants.
• Create a point plot for elevation vs horizontal distance to
roadways
a<-ggplot(df_sample, aes(y=Elevation, x=Horizontal_Distance_To_Roadways, size = Slope, colour = Cover_Type)) + geom_point(alpha = 0.5) + labs(title = "Distribution of Cover types near roadways")
a

• Facet the above plot by wilderness area
a+facet_wrap(Combined_Wilderness_Area~., scales = "free")

ggplotly(a)
Warning: Using size for a discrete variable is not advised.
• Create a histogram for Horizontal_Distance_To_Fire_Points and facet
it with Cover_Type and Combined_Wilderness_Area
ggplot(df_sample, aes(Horizontal_Distance_To_Fire_Points)) + geom_histogram() +
facet_grid(Combined_Wilderness_Area~Cover_Type) +
labs(title="Areas prone to forest fire")

From the graph above, it seems that Rawah Area and Comanache Peak
Area are prone to wild fires. Specially species of plant like Spruce and
Lodgepole Pine are planted near those areas rather than any other
species.
ggplot(df_sample, aes(x=Horizontal_Distance_To_Fire_Points,y=Horizontal_Distance_To_Hydrology, colour = Cover_Type)) + geom_point() +
facet_wrap(.~Combined_Wilderness_Area)

ggplot(df_sample, aes(x=Horizontal_Distance_To_Roadways, y=Horizontal_Distance_To_Fire_Points, colour = Cover_Type)) +
geom_point() +facet_wrap(Combined_Wilderness_Area~.)

From the graph it can be seen that in Cache la Poudre Area is closer
to Roadways and Fireplaces but the plants are at a scare amount nut in
Neota Area, Spruce/Fir are closest to roadways and fire points but also
at an scarce amount. On the other hand, in Comanche Peak Area
Spruce/Fir, Lodgepole pine are present at a high amount and are closer
to roadways and fire point but Aspen and Douglas fir like plants are
planted further awat from those points. The same could be said for the
Rawah Area except for this area Spruce/Fir and Aspen are planted close
to fire points.
ggplot(df_sample, aes(x=Horizontal_Distance_To_Hydrology, y=Horizontal_Distance_To_Fire_Points)) +
geom_point()

https://roh4n.shinyapps.io/Rohan_Kalu_23189640/
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogUm9oYW4gS2FsdQ0KLS0tDQoNCuKAoiBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShzY2FsZXMpDQpgYGANCg0KDQrigKIgTG9hZCB0aGUgZGF0YXNldA0KYGBge3J9DQpkZiA8LSByZWFkLmNzdigiRm9yZXN0L2ZvcmVzdC5jc3YiKQ0KaGVhZChkZikNCmBgYA0KDQrigKIgQ2hlY2sgZm9yIGFueSBudWxsIHZhbHVlcw0KYGBge3J9DQphbnkoaXMubmEoZGYpKQ0KYGBgDQoNCiMgRGF0YSBDbGVhbmluZw0K4oCiIENvbWJpbmUgYWxsIHRoZSBzb2lsIHR5cGVzIGludG8gYSBzaW5nbGUgY29sdW1uDQpgYGB7cn0NCiMgU2luY2UgdGhlcmUgYXJlIDQwIG9mIHRoZSBjb2x1bW5zIGRlZGljYXRlZCBvbmx5IHRvIHNvaWwgdHlwZSwgaXQgd2FzIG5lY2Vzc2FyeSB0byByZWR1Y2UgdGhvc2UgY29sdW1ucyBpbnRvIG9uZQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZShDb21iaW5lZF9Tb2lsX1R5cGUgPSBjYXNlX3doZW4oDQogICAgU29pbF9UeXBlMSA9PSAxIH4gIjEiLA0KICAgIFNvaWxfVHlwZTIgPT0gMSB+ICIyIiwNCiAgICBTb2lsX1R5cGUzID09IDEgfiAiMyIsDQogICAgU29pbF9UeXBlNCA9PSAxIH4gIjQiLA0KICAgIFNvaWxfVHlwZTUgPT0gMSB+ICI1IiwNCiAgICBTb2lsX1R5cGU2ID09IDEgfiAiNiIsDQogICAgU29pbF9UeXBlNyA9PSAxIH4gIjciLA0KICAgIFNvaWxfVHlwZTggPT0gMSB+ICI4IiwNCiAgICBTb2lsX1R5cGU5ID09IDEgfiAiOSIsDQogICAgU29pbF9UeXBlMTAgPT0gMSB+ICIxMCIsDQogICAgU29pbF9UeXBlMTEgPT0gMSB+ICIxMSIsDQogICAgU29pbF9UeXBlMTIgPT0gMSB+ICIxMiIsDQogICAgU29pbF9UeXBlMTMgPT0gMSB+ICIxMyIsDQogICAgU29pbF9UeXBlMTQgPT0gMSB+ICIxNCIsDQogICAgU29pbF9UeXBlMTUgPT0gMSB+ICIxNSIsDQogICAgU29pbF9UeXBlMTYgPT0gMSB+ICIxNiIsDQogICAgU29pbF9UeXBlMTcgPT0gMSB+ICIxNyIsDQogICAgU29pbF9UeXBlMTggPT0gMSB+ICIxOCIsDQogICAgU29pbF9UeXBlMTkgPT0gMSB+ICIxOSIsDQogICAgU29pbF9UeXBlMjAgPT0gMSB+ICIyMCIsDQogICAgU29pbF9UeXBlMjEgPT0gMSB+ICIyMSIsDQogICAgU29pbF9UeXBlMjIgPT0gMSB+ICIyMiIsDQogICAgU29pbF9UeXBlMjMgPT0gMSB+ICIyMyIsDQogICAgU29pbF9UeXBlMjQgPT0gMSB+ICIyNCIsDQogICAgU29pbF9UeXBlMjUgPT0gMSB+ICIyNSIsDQogICAgU29pbF9UeXBlMjYgPT0gMSB+ICIyNiIsDQogICAgU29pbF9UeXBlMjcgPT0gMSB+ICIyNyIsDQogICAgU29pbF9UeXBlMjggPT0gMSB+ICIyOCIsDQogICAgU29pbF9UeXBlMjkgPT0gMSB+ICIyOSIsDQogICAgU29pbF9UeXBlMzAgPT0gMSB+ICIzMCIsDQogICAgU29pbF9UeXBlMzEgPT0gMSB+ICIzMSIsDQogICAgU29pbF9UeXBlMzIgPT0gMSB+ICIzMiIsDQogICAgU29pbF9UeXBlMzMgPT0gMSB+ICIzMyIsDQogICAgU29pbF9UeXBlMzQgPT0gMSB+ICIzNCIsDQogICAgU29pbF9UeXBlMzUgPT0gMSB+ICIzNSIsDQogICAgU29pbF9UeXBlMzYgPT0gMSB+ICIzNiIsDQogICAgU29pbF9UeXBlMzcgPT0gMSB+ICIzNyIsDQogICAgU29pbF9UeXBlMzggPT0gMSB+ICIzOCIsDQogICAgU29pbF9UeXBlMzkgPT0gMSB+ICIzOSIsDQogICAgU29pbF9UeXBlNDAgPT0gMSB+ICI0MCINCiAgKSkNCmBgYA0KDQrigKIgRG9pbmcgdGhlIHNhbWUgZm9yIHdpbGRlcm5lc3MgYXJlYQ0KYGBge3J9DQpkZiA8LSBkZiAlPiUNCiAgbXV0YXRlKENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYSA9IGNhc2Vfd2hlbigNCiAgICBXaWxkZXJuZXNzX0FyZWExID09IDEgfiAiUmF3YWggQXJlYSIsDQogICAgV2lsZGVybmVzc19BcmVhMiA9PSAxIH4gIk5lb3RhIEFyZWEiLA0KICAgIFdpbGRlcm5lc3NfQXJlYTMgPT0gMSB+ICJDb21hbmNoZSBQZWFrIEFyZWEiLA0KICAgIFdpbGRlcm5lc3NfQXJlYTQgPT0gMSB+ICJDYWNoZSBsYSBQb3VkcmUgQXJlYSINCiAgKSkNCmBgYA0KDQoNCuKAoiBTdW1taW5nIHVwIGFsbCBoaWxsc2hhZGUgaW50byBvbmUNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZShUb3RhbF9IaWxsc2hhZGUgPSBIaWxsc2hhZGVfOWFtICsgSGlsbHNoYWRlX05vb24gKyBIaWxsc2hhZGVfM3BtKQ0KYGBgDQoNCg0K4oCiIENoYW5nZSBkZWdyZWVzIGludG8gY2FyZGluYWwgZGlyZWN0aW9ucw0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIGNvbnZlcnQgQXNwZWN0IGludG8gY2FyZGluYWwgYW5kIGludGVybWVkaWF0ZSBkaXJlY3Rpb25zDQpnZXRfY2FyZGluYWxfZGlyZWN0aW9uX2Z1bGwgPC0gZnVuY3Rpb24oYXNwZWN0KSB7DQogIGlmICghaXMubmEoYXNwZWN0KSAmJiBhc3BlY3QgPj0gMCAmJiBhc3BlY3QgPCAzNjApIHsNCiAgICBpZiAoYXNwZWN0ID49IDMzNy41IHx8IGFzcGVjdCA8IDIyLjUpIHsNCiAgICAgIHJldHVybigiTiIpDQogICAgfSBlbHNlIGlmIChhc3BlY3QgPj0gMjIuNSAmJiBhc3BlY3QgPCA2Ny41KSB7DQogICAgICByZXR1cm4oIk5FIikNCiAgICB9IGVsc2UgaWYgKGFzcGVjdCA+PSA2Ny41ICYmIGFzcGVjdCA8IDExMi41KSB7DQogICAgICByZXR1cm4oIkUiKQ0KICAgIH0gZWxzZSBpZiAoYXNwZWN0ID49IDExMi41ICYmIGFzcGVjdCA8IDE1Ny41KSB7DQogICAgICByZXR1cm4oIlNFIikNCiAgICB9IGVsc2UgaWYgKGFzcGVjdCA+PSAxNTcuNSAmJiBhc3BlY3QgPCAyMDIuNSkgew0KICAgICAgcmV0dXJuKCJTIikNCiAgICB9IGVsc2UgaWYgKGFzcGVjdCA+PSAyMDIuNSAmJiBhc3BlY3QgPCAyNDcuNSkgew0KICAgICAgcmV0dXJuKCJTVyIpDQogICAgfSBlbHNlIGlmIChhc3BlY3QgPj0gMjQ3LjUgJiYgYXNwZWN0IDwgMjkyLjUpIHsNCiAgICAgIHJldHVybigiVyIpDQogICAgfSBlbHNlIGlmIChhc3BlY3QgPj0gMjkyLjUgJiYgYXNwZWN0IDwgMzM3LjUpIHsNCiAgICAgIHJldHVybigiTlciKQ0KICAgIH0NCiAgfQ0KICByZXR1cm4oIk4iKSANCn0NCg0KIyBBcHBseSB0aGUgZnVuY3Rpb24gdG8gdGhlIEFzcGVjdCBjb2x1bW4gZGlyZWN0bHkNCmRmJEFzcGVjdCA8LSBzYXBwbHkoZGYkQXNwZWN0LCBnZXRfY2FyZGluYWxfZGlyZWN0aW9uX2Z1bGwpDQoNCiMgVmlldyB0aGUgZmlyc3QgZmV3IHJvd3MgdG8gY29uZmlybQ0KaGVhZChkZiRBc3BlY3QpDQoNCmBgYA0KDQrigKIgU2V0IHRoZSB2YWx1ZXMgZm9yIGNvdmVyIHR5cGUNCmBgYHtyfQ0KY292ZXJfdHlwZV9tYXBwaW5nIDwtIGMoDQogIGAxYCA9ICJTcHJ1Y2UvRmlyIiwNCiAgYDJgID0gIkxvZGdlcG9sZSBQaW5lIiwNCiAgYDNgID0gIlBvbmRlcm9zYSBQaW5lIiwNCiAgYDRgID0gIkNvdHRvbndvb2QvV2lsbG93IiwNCiAgYDVgID0gIkFzcGVuIiwNCiAgYDZgID0gIkRvdWdsYXMtZmlyIiwNCiAgYDdgID0gIktydW1taG9seiINCikNCg0KIyBSZXBsYWNlIHRoZSBDb3Zlcl9UeXBlIHZhbHVlcyBpbiB0aGUgZGF0YWZyYW1lDQpkZiRDb3Zlcl9UeXBlIDwtIGZhY3Rvcihjb3Zlcl90eXBlX21hcHBpbmdbYXMuY2hhcmFjdGVyKGRmJENvdmVyX1R5cGUpXSwgbGV2ZWxzID0gY292ZXJfdHlwZV9tYXBwaW5nKQ0KDQojIFZpZXcgdGhlIGZpcnN0IGZldyByb3dzIHRvIGNvbmZpcm0gdGhlIGNoYW5nZXMNCmhlYWQoZGYkQ292ZXJfVHlwZSkNCg0KYGBgDQoNCuKAoiBTaW5jZSB0aGUgU2xvcGUgcmFuZ2VzIGZyb20gMCB0byA1MCwgd2UgY2FuIG1ha2UgaXQgaW50byBjb250aW51b3VzIHZhbHVlIHNvIHRoYXQgd2hpbGUgcGxvdHRpbmcgaXQgbWFrZXMgaXQgZWFzaWVyIGFuZCBub3QgdmVyeSBtZXNzeQ0KYGBge3J9DQojIE1vZGlmeSB0aGUgU2xvcGUgY29sdW1uIHRvIGdyb3VwIHZhbHVlcyBpbiBiaW5zIG9mIDUNCmRmJFNsb3BlIDwtIGN1dChkZiRTbG9wZSwgDQogICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIG1heChkZiRTbG9wZSwgbmEucm0gPSBUUlVFKSwgYnkgPSA1KSwgDQogICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLCANCiAgICAgICAgICAgICAgICByaWdodCA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZShzZXEoMCwgbWF4KGRmJFNsb3BlLCBuYS5ybSA9IFRSVUUpIC0gNSwgYnkgPSA1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VxKDUsIG1heChkZiRTbG9wZSwgbmEucm0gPSBUUlVFKSwgYnkgPSA1KSwgc2VwID0gIi0iKSkNCmBgYA0KDQoNCuKAoiBDaGVjayBkaXN0cmlidXRpb24gZm9yIEVsZXZhdGlvbg0KYGBge3J9DQphIDwtIGdncGxvdChkZiwgYWVzKEVsZXZhdGlvbikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ic3RlZWxibHVlIikrDQogIGxhYnModGl0bGU9ICJEaXN0cmlidXRpb24gb2YgRWxldnZhdGlvbiIpDQphDQpgYGANCg0KYGBge3J9DQphK2ZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4pICsNCiAgIGxhYnModGl0bGUgPSAiV2lsZGVybmVzcyBBcmVhcyBhY3Jvc3MgZGlmZmVyZW50IGVsZXZhdGlvbiBsZXZlbHMiKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyhFbGV2YXRpb24pKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGw9InN0ZWVsYmx1ZSIpKw0KICBsYWJzKHRpdGxlPSAiRGlzdHJpYnV0aW9uIG9mIEVsZXZhdGlvbiIpICsNCiAgZmFjZXRfd3JhcChBc3BlY3R+LikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoeCA9IEVsZXZhdGlvbikpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIEVsZXZhdGlvbiIsIHggPSAiRWxldmF0aW9uIiwgeSA9ICJEZW5zaXR5IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCuKAoiBTaW5jZSBvdXIgZGF0YSBzZXQgaXMgdG9vIGxhcmdlIGZvciBwcm9wZXIgdmlzdWFsaXphdGlvbiwgd2Ugd2lsbCB0YWtlIGEgc21hbGwgc2FtcGxlIG9mIHRoZSBkYXRhIHRoYXQgcmVzZW1ibGVzIHRoZSB0cmVuZCBvZiB0aGUgb3JpZ2luYWwgZGF0YS4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKSAgIyBTZXR0aW5nIGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpkZl9zYW1wbGUgPC0gZGYgJT4lIHNhbXBsZV9uKDMwMDApDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoeCA9IEVsZXZhdGlvbikpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIEVsZXZhdGlvbiIsIHggPSAiRWxldmF0aW9uIiwgeSA9ICJEZW5zaXR5IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCg0K4oCiIENoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgU2xvcGUNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoU2xvcGUpKSArDQogIGdlb21fYmFyKCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBTbG9wZSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIFNsb3BlIiwgeCA9ICJTbG9wZSIsIHkgPSAiRGVuc2l0eSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4ID0gU2xvcGUpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBTbG9wZSIsIHggPSAiU2xvcGUiLCB5ID0gIkRlbnNpdHkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANClNpbmNlIHRoZSBkZW5zaXR5IG9mIHNsb3BlIGFuZCBlbGV2YXRpb24gbG9va3Mgc2ltaWxhciB0byB0aGF0IG9mIHRoZSByZWFsIGRhdGFzZXQsIHdlIHdpbGwgY29uY2x1ZGUgdGhhdCBvdGhlciBmZWF0dXJlIHdpbGwgZm9sbG93IHRoZSBzYW1lIHRyZW5kLg0KDQoNCuKAoiBEaXN0cmlidXRpb24gb2YgQ292ZXIgVHlwZSB0aHJvdWdob3V0IHRoZSBkYXRhIHNldA0KYGBge3J9DQphPC1nZ3Bsb3QoZGYsIGFlcyhDb3Zlcl9UeXBlKSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSsNCiAgZ2VvbV9iYXIoZmlsbD0ic3RlZWxibHVlIikrDQogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgDQogICAgICAgICAgICB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb3ZlciBUeXBlIiwNCiAgICAgICB4PSJDb3ZlciBUeXBlIiwNCiAgICAgICB5ID0gIkNvdW50IikNCmENCmBgYA0KDQoNCuKAoiBGYWNldCB0aGUgY292ZXIgZGlzdHJpYnV0aW9uIGFjY29yZGluZyB0byBXaWxkZXJuZXNzIEFyZWENCmBgYHtyfQ0KYSArIGZhY2V0X2dyaWQoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4sIHNjYWxlcz0iZnJlZSIpDQpgYGANCg0KYGBge3J9DQphK2ZhY2V0X3dyYXAoQXNwZWN0fi4pDQpgYGANCg0K4oCiIENoZWNrIGhvdyBtdWNoIHN1bmxpZ2h0IGRvZXMgZWFjaCBkaXJlY3Rpb24gcmVjZWl2ZSBhdCA5YW0NCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoSGlsbHNoYWRlXzlhbSkpKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJzdGVlbGJsdWUiKSArDQpmYWNldF93cmFwKEFzcGVjdH4uKSArDQogIGxhYnModGl0bGUgPSAiSGlsbHNoYWRlIGF0IDlhbSIpDQpgYGANCkVhc3QgYW5kIE5vcnRoIHJlY2VpdmVkIHRoZSBtb3N0IHN1bmxpZ2h0IGF0IDlhbS4NCg0KDQrigKIgQ2hlY2sgaG93IG11Y2ggc3VubGlnaHQgZG9lcyBlYWNoIGRpcmVjdGlvbiByZWNlaXZlIGR1cmluZyBub29uDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKEhpbGxzaGFkZV9Ob29uKSkrDQogIGdlb21faGlzdG9ncmFtKGZpbGw9InN0ZWVsYmx1ZSIpICsNCiAgZmFjZXRfd3JhcChBc3BlY3R+LikgKw0KICBsYWJzKHRpdGxlID0gIkhpbGxzaGFkZSBkdXJpbmcgTm9vbiIpDQpgYGANCkR1cmluZyB0aGUgbm9vbiB0aW1lLCBvbmx5IHNvdXRoIHdlc3QgYW5kIHdlc3QgcmVjZWl2ZWQgdGhlIGxlYXN0IGFtb3VudCBvZiBzdW5saWdodC4NCg0KDQrigKIgQ2hlY2sgaG93IG11Y2ggc3VubGlnaHQgZG9lcyBlYWNoIGRpcmVjdGlvbiByZWNlaXZlIGF0IDNwbQ0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyhIaWxsc2hhZGVfM3BtKSkrDQogIGdlb21faGlzdG9ncmFtKGZpbGw9InN0ZWVsYmx1ZSIpICsNCiAgZmFjZXRfd3JhcChBc3BlY3R+LikgKw0KICBsYWJzKHRpdGxlID0gIkhpbGxzaGFkZSBhdCAzcG0iKQ0KYGBgDQpGcm9tIGFsbCB0aGUgcGxvdHMgYWJvdmUsIGl0IGlzIGNsZWFyIHRoYXQgd2VzdCBhbmQgc291dGh3ZXN0IHJlY2VpdmVkIHRoZSBsZWFzdCBhbW91bnQgb2Ygc3VubGlnaHQuDQoNCg0KDQrigKIgUmVtb3ZlIHRoZSB1bm5lY2Vzc2FyeSBjb2x1bW5zDQpTaW5jZSBvdXIgZGF0YSBzZXQgYWxyZWFkeSBoYWQgYSBtYXNzaXZlIHNldCBvZiBjb2x1bW5zIGFuZCB0aGUgY29sdW1ucyAnU29pbF9UeXBlJyBhbmQgJ1dpbGRlcm5lc3NfQXJlYScgaGF2ZSBiZWVuIGNhdGVnb3JpemVkIGludG8gdGhlIGNvbHVtbiAnQ29tYmluZWRfU29pbF9UeXBlJyBhbmQgJ0NvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYScgdGhleSBhcmUgdW5uZWNlc3NhcnkNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIHNlbGVjdCgtc3RhcnRzX3dpdGgoIlNvaWxfVHlwZSIpLCAtc3RhcnRzX3dpdGgoIldpbGRlcm5lc3NfQXJlYSIpLCAtc3RhcnRzX3dpdGgoIkhpbGxzaGFkZSIpKSAjUmVtb3ZlIGFueSBjb2x1bW4gdGhhdCBzdGFydHMgd2l0aCB0aGUgd29yZCBTb2lsX1R5cGUsIFdpbGRlcm5lc3NfQXJlYSBhbmQgSGlsbHNoYWRlDQpgYGANCg0K4oCiIENoZWNraW5nIHRvIHNlZSBpZiB0aGUgY2hhbmdlcyBoYXZlIGJlZW4gYXBwbGllZA0KYGBge3J9DQpkZg0KYGBgDQoNCg0KYGBge3J9DQphK2ZhY2V0X3dyYXAoLn5Db21iaW5lZF9XaWxkZXJuZXNzX0FyZWEpDQpgYGANCg0KDQoNCuKAoiBQbG90IGRlbnNpdHkgZ3JhcGggZm9yIGVsZXZhdGlvbiB2cyBjb3ZlciB0eXBlDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBFbGV2YXRpb24sIGZpbGwgPSBmYWN0b3IoQ292ZXJfVHlwZSkpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgb2YgRWxldmF0aW9uIGJ5IENvdmVyIFR5cGUiLCB4ID0gIkVsZXZhdGlvbiIsIHkgPSAiRGVuc2l0eSIpDQoNCmBgYA0KV2UgY2FuIHNlZSB0aGF0IENvdHRvbndvb2QgZ3Jvd3MgYXQgbG93ZXIgYWx0aXR1ZGUgYW5kIEtydW1taG9seiBncm93cyBhdCB0aGUgaGlnaGVyIGFsdGl0dWRlLg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHg9Q292ZXJfVHlwZSwgeT1FbGV2YXRpb24pKStnZW9tX2JveHBsb3QoKQ0KYGBgDQoNCg0K4oCiIENoZWNrIGRpc3RyaWJ1dGlvbiBmb3Igc29pbCB0eXBlDQpgYGB7cn0NCnA8LWdncGxvdChkZiwgYWVzKHggPSBDb21iaW5lZF9Tb2lsX1R5cGUpKSArDQogIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb21iaW5lZCBTb2lsIFR5cGVzIiwgeCA9ICJTb2lsIFR5cGUiLCB5ID0gIkNvdW50IikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpDQpwDQpgYGANCkZyb20gdGhlIGJhciBwbG90LCBpdCBjYW4gYmUgc2VlbiB0aGF0IHRoZSBtb3N0IHR5cGUgb2YgcHJldmFsZW50IHNvaWwgaXMgb2YgdHlwZSAyOSB3aGljaCBpcyBhbG1vc3QgMTIwMDAwMC4gT24gdGhlIG90aGVyIGhhbmQsIE1vc3Qgb2YgdGhlIHNvaWwgdHlwZXMgYXJlIGFsbW9zdCBub24tZXhpc3RlbnQgbGlrZSBzb2lsX1R5cGUgMTMsMTQsMzcgYW5kIG1hbnkgbW9yZSB3aGljaCBpbmRpY2F0ZXMgdGhlaXIgcHJlc2VuY2UgaW4gc29tZXdoYXQgc21hbGwgYW1vdW50IGNvbXBhcmVkIHRvIG90aGVycy4NCg0KDQrigKIgRmFjZXQgc29pbCB0eXBlIGRpc3RyaWJ1dGlvbiBieSB3aWxkZXJuZXNzIGFyZWENCmBgYHtyfQ0KcCtmYWNldF93cmFwKENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYX4uKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFNvaWwgVHlwZSBhY3Jvc3MgV2lsZGVybmVzcyBBcmVhcyIpDQpgYGANClJhd2FoIEFyZWEgcmVjZWl2ZXMgaGFzIHRoZSBtb3N0IGFtb3VudCBvZiBzb2lscyB3aGlsZSBDb21hbmFjaGUgQXJlYSBoYXMgdGhlIG1vc3QgdmFyaWV0eSBvZiBzb2lscy4NCg0KDQrigKIgDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBDb3Zlcl9UeXBlLCB5ID0gVG90YWxfSGlsbHNoYWRlLCBmaWxsID0gQ292ZXJfVHlwZSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIEhpbGxzaGFkZSAoU3VubGlnaHQpIERpc3RyaWJ1dGlvbiBieSBDb3ZlciBUeXBlIiwNCiAgICAgICB4ID0gIkNvdmVyIFR5cGUiLA0KICAgICAgIHkgPSAiVG90YWwgSGlsbHNoYWRlIChTdW5saWdodCkiKSArIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgZmFjZXRfZ3JpZChDb21iaW5lZF9XaWxkZXJuZXNzX0FyZWF+LikNCg0KYGBgDQpGcm9tIHRoZSBncmFwaCBhYm92ZSwgaXQgc2VlbXMgdGhhdCBldmVyeSB0eXBlIG9mIHBsYW50IGNhbiBncm93IHdoZW4gcmVjZWl2aW5nIG1vcmUgc3VubGlnaHQgYnV0IExvZGdlcG9sZSBQaW5lIGNhbiBncm93IG9uIGFyZWFzIHdpdGggbG93IHN1bmxpZ2h0IG9mIHVwdG8gb25seSAyMDAgdW5pdHMuDQoNCg0K4oCiIENyZWF0ZSBhIGJhciBmb3Igc29pbCB0eXBlIGFuZCBzdGFjayBpdCB3aXRoIGNvdmVyIHR5cGUNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoeCA9IENvbWJpbmVkX1NvaWxfVHlwZSwgZmlsbCA9IENvdmVyX1R5cGUpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIikgKw0KICBsYWJzKHRpdGxlID0gIkNvdmVyIFR5cGUgYW5kIFNvaWwgVHlwZSIsIHggPSAiU29pbCBUeXBlIiwgeSA9ICJDb3VudCIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKQ0KYGBgDQpGcm9tIHRoZSBiYXIgcGxvdCB3ZSBjYW4gc2VlIHRoYXQgc29pbCAyOSBpcyB0aGUgbW9zdCBzdWl0YWJsZSBmb3Igc3BydWNlL2ZpciB0cmVlIGFuZCBMb2RnZXBvbGUgUGluZSB0cmVlIHdoaWNoIGFyZSBhbHNvIHRoZSBtb3N0IHByZXNlbnQgdHlwZSBvZiB0cmVlcyBvbiBvdGhlciBzb2lscyBjb21wYXJlZCB0byBvdGhlciBwbGFudHMgaW5kaWNhdGluZyB0aGVpciBjYXBhY2l0eSB0byBncm93IG9uIGFueSB0eXBlIG9mIHNpdHVhdGlvbi4NCg0KDQrigKIgIEZhY2V0IGl0IHdpdGggV2lsZGVybmVzcyBBcmVhDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBDb21iaW5lZF9Tb2lsX1R5cGUsIGZpbGwgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJDb3ZlciBUeXBlIGFuZCBTb2lsIFR5cGUiLCB4ID0gIlNvaWwgVHlwZSIsIHkgPSAiQ291bnQiKSArDQogIGZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4pKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpDQpgYGANCg0KDQrigKIgQ2hlY2sgdG8gc2VlIGlmIHRoZSByZWxhdGlvbiBiZXR3ZWVuIHNvaWwgdHlwZSBhbmQgaHlkcm9sb2d5DQpgYGB7cn0NCmI8LWdncGxvdChkZiwgYWVzKHg9SG9yaXpvbnRhbF9EaXN0YW5jZV9Ub19IeWRyb2xvZ3ksIHk9VmVydGljYWxfRGlzdGFuY2VfVG9fSHlkcm9sb2d5KSkgKyBnZW9tX3Ntb290aCgpICsgZmFjZXRfd3JhcCh+Q29tYmluZWRfU29pbF9UeXBlLCBzY2FsZXM9ImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAiVHlwZSBvZiBTb2lsIGF2YWlsYWJsZSBuZWFyIHdhdGVyIGJvZGllcyIpDQpgYGANCkZyb20gdGhlIHBsb3RzIGl0IGlzIGhhcmQgdG8gZGV0ZXJtaW5lIHJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byBhcyBldmVyeSBzb2lsIHR5cGUgc2VlbSB0byBiZSBhdmFpbGFibGUgYXQgYWxtb3N0IGV2ZXJ5IGhvcml6b250YWwgZGlzdGFuY2UgYXdheSBmcm9tIGEgYm9keSBvZiB3YXRlci4gU29tZSBsaWtlIHR5cGUgMSwgMTYsIDE4LCAyLCAyMSwgOCwgOSwgNSwgNiwgNyBpcyBmb3VuZCBpbiB0aGUgcmFuZ2Ugb2YgNjAwbSBhd2F5IGZyb20gdGhlIGJvZHkgb2Ygd2F0ZXIsIHRoZSBvdGhlciB0eXBlcyBhcmUgYXZhaWxhYmxlIDEwMDAgbWV0ZXJzIGF3YXkuIA0KQXMgZm9yIHRoZSB2ZXJ0aWNhbCBkaXN0YW5jZSBmcm9tIGh5ZHJvbG9neSwgbW9zdCBhcmUgZm91bmQgYXQgdGhlIHJhbmdlIG9mIDEwMC0xNTAgbWV0ZXJzIGFib3ZlIGFuZCBzb21lIGxpa2UgOCBhbmQgMjUgYXJlIGZvdW5kIGJlbG93IHRoZSBib2R5IG9mIHdhdGVyLiBTb2lsIHR5cGUgNCwgMjQsIDI3LCAyOCwgMzEsIDMyLCAzOCwgMzkgYW5kIDQwIGNvdWxkIGJlIGZvdW5kIGFib3ZlIDIwMG0gYWJvdmUgYW55IGJvZHkgb2Ygd2F0ZXIgYnV0IHNvaWwgdHlwZSBsaWtlIDcsIDgsIDkgYW5kIDI1IGFyZSBmb3VuZCBhcyBjbG9zZSBhcyA0MCBtZXRlciBhbmQgZXZlbiBwb3RyYXllZCBpbiBuZWdhdGl2ZSBpbmRpY2F0aW5nIHRoZW0gYmVpbmcgc3VibWVyZ2VkIGluIHRoZSBib2R5IG9mIHdhdGVyDQoNCg0K4oCiIEFkZCBuZXcgZmVhdHVyZSAnQ292ZXJfVHlwZScgYXMgY29sb3VyDQpgYGB7cn0NCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4PUhvcml6b250YWxfRGlzdGFuY2VfVG9fSHlkcm9sb2d5LCB5PVZlcnRpY2FsX0Rpc3RhbmNlX1RvX0h5ZHJvbG9neSwgY29sb3VyID0gQ292ZXJfVHlwZSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+Q29tYmluZWRfU29pbF9UeXBlLCBzY2FsZXM9ImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAiQ292ZXIgdHlwZSBhbmQgc291cmNlcyBvZiB3YXRlciIpDQpgYGANCg0K4oCiIFBvaW50IGdyYXBoIHdpdGggRWxldmF0aW9uIHZzIFNvaWwgVHlwZQ0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoeT1FbGV2YXRpb24sIHg9Q29tYmluZWRfU29pbF9UeXBlLCBjb2xvdXIgPSBDb3Zlcl9UeXBlKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgbGFicyh0aXRsZSA9ICJFbGV2YXRpb24gdnMgU29pbCBUeXBlIikNCmBgYA0KQXMgdGhlIHR5cGUgb2Ygc29pbCBpbmNyZWFzZXMgc28gZG9lcyB0aGUgZWxldmF0aW9uLg0KDQrigKINCmBgYHtyfQ0KDQojIENyZWF0ZSBhIGJveHBsb3QNCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4ID0gRWxldmF0aW9uLCB5ID0gVG90YWxfSGlsbHNoYWRlLCBjb2xvdXIgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiU3VubGlnaHQgRXhwb3N1cmUgKEhpbGxzaGFkZSkgdnMgRWxldmF0aW9uIiwNCiAgICAgICB4ID0gIkVsZXZhdGlvbiIsDQogICAgICAgeSA9ICJUb3RhbCBIaWxsc2hhZGUgKFN1bmxpZ2h0KSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICBmYWNldF9ncmlkKENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYX4uKQ0KDQpgYGANCg0K4oCiIENoZWNrIHRoZSANCmBgYHtyfQ0KZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHggPSBFbGV2YXRpb24sIHkgPSBUb3RhbF9IaWxsc2hhZGUpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgbGFicyh0aXRsZSA9ICJTdW5saWdodCBFeHBvc3VyZSAoSGlsbHNoYWRlKSB2cyBFbGV2YXRpb24iLA0KICAgICAgIHggPSAiRWxldmF0aW9uIiwNCiAgICAgICB5ID0gIlRvdGFsIEhpbGxzaGFkZSAoU3VubGlnaHQpIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0K4oCiDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHk9VG90YWxfSGlsbHNoYWRlLHggPUNvdmVyX1R5cGUsIGZpbGwgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKQ0KYGBgDQoNCg0KDQrigKIgUGxvdCBhIGhpc3RvZ3JhbSBmb3JoaWxsc2hhZGUgYW5kIGZhY2V0IGl0IHdpdGggd2lsZGVybmVzcyBhcmVhIGFuZCBjb3ZlciB0eXBlDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBUb3RhbF9IaWxsc2hhZGUpKSArDQogIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImRvZGdlIiwgYmlud2lkdGggPSAxMCwgZmlsbD0ic3RlZWxibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb3Zlcl9UeXBlIiwgeCA9ICJIaWxsIFNoYWRlKFN1bmxpZ2h0KSIsIHkgPSAiQ291bnQiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KCBoanVzdCA9IDEpKSArIA0KICBmYWNldF9ncmlkKENvdmVyX1R5cGV+Q29tYmluZWRfV2lsZGVybmVzc19BcmVhLCBzY2FsZXMgPSAiZnJlZSIpDQoNCmBgYA0KRnJvbSB0aGUgcGxvdCwgd2UgY2FuIHNlZSB0aGF0IHNvbWUgd2lsZGVybmVzcyBhcmVhIGlzIHVuc3VpdGFibGUgZm9yIHNvbWUgdHlwZSBvZiBmb3Jlc3QgYXMgdGhleSBkbyBub3QgZ3JvdyB0aGVyZSBhdCBhbGwuIEVnLCBTcHJ1Y2UgYW5kICAuLi4uLi4gZG8gbm90IGdyb3cgb24gd2lsZGVybmVzcyBhcmVhIDQgYXQgYWxsIGJ1dCAuLi4ud29vZCBvbmx5IGdyb3dzIG9uIHdpbGRlcm5lc3MgYXJlYSA0Lg0KSWYgd2Ugd2VyZSB0byBsb29rIGF0IHRoZSBwbG90IGFsb25lIGl0IHdvdWxkIHNlZW0gbGlrZSBXaWxkZXJuZXNzX0FyZWE0IGhvc3RzIHRoZSBtb3N0IGZvcmVzdCBidXQgaWYgd2UgbG9vayBhdCB0aGUgY291bnQgdGhlbiB3ZSBjYW4gc2VlIHRoYXQgV2lsZGVybmVzc19BcmVhMSBob3N0cyB0aGUgbW9zdCBmb3Jlc3QuDQpJZiB3ZSB3ZXJlIHRvIGRldGVybWluZSB0aGUgY29uZGl0aW9ucyBvZiB0aGUgV2lsZGVybmVzcyBBcmVhIHRoZW4gd2UgY2FuIGRldGVybWluZSB0aGUgdHlwZSBvZiBmb3Jlc3QgdGhhdCBncm93cyBvbiBzdWNoIHRlcnJhaW5zLg0KDQoNCg0KYGBge3J9DQpxIDwtIGdncGxvdChkZiwgYWVzKHggPSBDb21iaW5lZF9XaWxkZXJuZXNzX0FyZWEsIGZpbGwgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgQ292ZXIgVHlwZSBhY2NvcmRpbmcgdG8gV2lsZGVybmVzcyBBcmVhIiwNCiAgICAgICB4ID0gIldpbGRlcm5lc3MgQXJlYSIsDQogICAgICAgeSA9ICJDb3VudCIpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgc2l6ZSA9IDMsIGNvbG9yID0gIndoaXRlIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KcQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90bHkocSkNCmBgYA0KRnJvbSB0aGUgYmFyIGdyYXBoLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1vc3QgcHJldmFsZW50IHR5cGUgb2YgZm9yZXN0IGlzIExvZGdlcG9sZSBQaW5lIGZvbGxvd2VkIGJ5IFNwcnVjZS9GaXIgd2hpY2ggYXJlIGF0IGFuIG5vdGljZWFibGUgYW1vdW50IGZvbGxvd2VkIGJ5IFBvbmRlcm9zYSBQaW5lIGFuZCBzbyBvbi4gV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIFJhd2FoIEFyZWEgaG9zdHMgdGhlIG1vc3QgbnVtYmVyIG9mIHBsYW50cyB3aGlsZSB0aGUgQ29tYW5jaGUgUGVhayBBcmVhIGhvc3RzIHRoZSBtb3N0IHR5cGUgb2YgcGxhbnRzLg0KDQoNCuKAoiBDcmVhdGUgYSBwb2ludCBwbG90IGZvciBlbGV2YXRpb24gdnMgaG9yaXpvbnRhbCBkaXN0YW5jZSB0byByb2Fkd2F5cyANCmBgYHtyfQ0KYTwtZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHk9RWxldmF0aW9uLCB4PUhvcml6b250YWxfRGlzdGFuY2VfVG9fUm9hZHdheXMsIHNpemUgPSBTbG9wZSwgY29sb3VyID0gQ292ZXJfVHlwZSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb3ZlciB0eXBlcyBuZWFyIHJvYWR3YXlzIikNCmENCmBgYA0K4oCiIEZhY2V0IHRoZSBhYm92ZSBwbG90IGJ5IHdpbGRlcm5lc3MgYXJlYQ0KYGBge3J9DQphK2ZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4sIHNjYWxlcyA9ICJmcmVlIikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdGx5KGEpDQpgYGANCg0KDQrigKIgQ3JlYXRlIGEgaGlzdG9ncmFtIGZvciBIb3Jpem9udGFsX0Rpc3RhbmNlX1RvX0ZpcmVfUG9pbnRzIGFuZCBmYWNldCBpdCB3aXRoIENvdmVyX1R5cGUgYW5kIENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYQ0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoSG9yaXpvbnRhbF9EaXN0YW5jZV9Ub19GaXJlX1BvaW50cykpICsgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGZhY2V0X2dyaWQoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfkNvdmVyX1R5cGUpICsNCiAgbGFicyh0aXRsZT0iQXJlYXMgcHJvbmUgdG8gZm9yZXN0IGZpcmUiKQ0KYGBgDQpGcm9tIHRoZSBncmFwaCBhYm92ZSwgaXQgc2VlbXMgdGhhdCBSYXdhaCBBcmVhIGFuZCBDb21hbmFjaGUgUGVhayBBcmVhIGFyZSBwcm9uZSB0byB3aWxkIGZpcmVzLiBTcGVjaWFsbHkgc3BlY2llcyBvZiBwbGFudCBsaWtlIFNwcnVjZSBhbmQgTG9kZ2Vwb2xlIFBpbmUgYXJlIHBsYW50ZWQgbmVhciB0aG9zZSBhcmVhcyByYXRoZXIgdGhhbiBhbnkgb3RoZXIgc3BlY2llcy4NCg0KDQpgYGB7cn0NCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4PUhvcml6b250YWxfRGlzdGFuY2VfVG9fRmlyZV9Qb2ludHMseT1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0h5ZHJvbG9neSwgY29sb3VyID0gQ292ZXJfVHlwZSkpICsgZ2VvbV9wb2ludCgpICsNCiAgZmFjZXRfd3JhcCgufkNvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYSkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHg9SG9yaXpvbnRhbF9EaXN0YW5jZV9Ub19Sb2Fkd2F5cywgeT1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0ZpcmVfUG9pbnRzLCBjb2xvdXIgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX3BvaW50KCkgK2ZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4pDQpgYGANCkZyb20gdGhlIGdyYXBoIGl0IGNhbiBiZSBzZWVuIHRoYXQgaW4gQ2FjaGUgbGEgUG91ZHJlIEFyZWEgaXMgY2xvc2VyIHRvIFJvYWR3YXlzIGFuZCBGaXJlcGxhY2VzIGJ1dCB0aGUgcGxhbnRzIGFyZSBhdCBhIHNjYXJlIGFtb3VudCBudXQgaW4gTmVvdGEgQXJlYSwgU3BydWNlL0ZpciBhcmUgY2xvc2VzdCB0byByb2Fkd2F5cyBhbmQgZmlyZSBwb2ludHMgYnV0IGFsc28gYXQgYW4gc2NhcmNlIGFtb3VudC4NCk9uIHRoZSBvdGhlciBoYW5kLCBpbiBDb21hbmNoZSBQZWFrIEFyZWEgU3BydWNlL0ZpciwgTG9kZ2Vwb2xlIHBpbmUgYXJlIHByZXNlbnQgYXQgYSBoaWdoIGFtb3VudCBhbmQgYXJlIGNsb3NlciB0byByb2Fkd2F5cyBhbmQgZmlyZSBwb2ludCBidXQgQXNwZW4gYW5kIERvdWdsYXMgZmlyIGxpa2UgcGxhbnRzIGFyZSBwbGFudGVkIGZ1cnRoZXIgYXdhdCBmcm9tIHRob3NlIHBvaW50cy4gVGhlIHNhbWUgY291bGQgYmUgc2FpZCBmb3IgdGhlIFJhd2FoIEFyZWEgZXhjZXB0IGZvciB0aGlzIGFyZWEgU3BydWNlL0ZpciBhbmQgQXNwZW4gYXJlIHBsYW50ZWQgY2xvc2UgdG8gZmlyZSBwb2ludHMuDQoNCg0KDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoeD1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0h5ZHJvbG9neSwgeT1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0ZpcmVfUG9pbnRzKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQpodHRwczovL3JvaDRuLnNoaW55YXBwcy5pby9Sb2hhbl9LYWx1XzIzMTg5NjQwLw0K